/**
 * 飞行游戏
 */
use std::{fmt::format, thread, time::Duration};
use bracket_lib::prelude::*; // bracket游戏库

// 定义游戏模式结构体
enum GameMode {
    Menu, // 显示菜单
    Playing, // 玩游戏中
    End, // 游戏结束
}

// 屏幕宽度
const SCREEN_WDITH: i32 = 80;
// 屏幕高度
const SCREEN_HEIGHT: i32 = 50;
// 操作间隔时长（毫秒）
const FRAME_DURATION: f32 = 75.0;
// 玩家移动速率
const STEP_DURATION: f32 = 0.2;

// 定义玩家结构体
struct Player {
    x: i32, // x坐标
    y: i32, // y坐标
    velocity: f32, // 移动
}

impl Player {
    // 初始化玩家
    fn new(x: i32, y: i32) -> Self {
        Player{
            x: 0,
            y: 0,
            velocity: 0.0,
        }
    }
    // 渲染玩家
    fn render(&mut self, ctx: &mut BTerm) {
        ctx.set(0, self.y, YELLOW, WHITE, to_cp437('⚪'))
    }

    // 玩家重力下降
    fn gravity_and_move(&mut self) {
        if self.velocity < 2.0 {
            self.velocity += STEP_DURATION;
        }
        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }

    // 玩家跳跃
    fn flap(&mut self) {
        self.velocity = -2.0;
    }

}
// 定义障碍物结构体
struct Obstacle {
    x: i32, // 障碍物横坐标
    gap_y: i32, // 障碍物间隙纵坐标
    size: i32, // 障碍物尺寸大小
}

impl Obstacle {
    // 初始化障碍物
    fn new(x: i32, score: i32) -> Self {
        let mut random = RandomNumberGenerator::new();
        Obstacle {
            x: x,
            gap_y: random.range(10, 40),
            size: i32::max(2, 20 - score),
        }
    }
    // 障碍物渲染
    fn render(&mut self, ctx: &mut BTerm, player_x: i32) {
        let screen_x = self.x - player_x;
        let half_size = self.size / 2;
        // 障碍物上半部分渲染
        for y in 0..self.gap_y - half_size {
            ctx.set(screen_x, y, RED, BLACK, to_cp437('|'));
        }
        // 障碍物下半部分渲染
        for y in self.gap_y + half_size..SCREEN_HEIGHT {
            ctx.set(screen_x, y, RED, BLACK, to_cp437('|'));
        }
    }

    // 判断玩家是否与障碍物碰撞
    fn hit_obstacle(&self, player:&Player) -> bool {
        let half_size = self.size / 2;
        let does_x_match = player.x == self.x;
        let player_above_gap = player.y < self.gap_y - half_size;
        let player_below_gap = player.y > self.gap_y + half_size;
        does_x_match && (player_above_gap || player_below_gap)
    }
}

// 游戏状态
struct State {
    player: Player, // 玩家
    frame_time: f32, // 时长
    mode: GameMode, // 游戏模式
    obstacle: Obstacle, // 障碍物
    score: i32, // 计分
}

impl State {
    // 初始化游戏状态
    fn new() -> Self {
        State {
            player: Player::new(5, 25),
            frame_time: 0.0,
            mode: GameMode::Menu,
            obstacle: Obstacle::new(SCREEN_WDITH, 0),
            score: 0,
        }  
    }

    // 游戏开始
    fn play(&mut self, ctx: &mut BTerm) {
        ctx.cls_bg(NAVY);
        self.frame_time += ctx.frame_time_ms;
        // 玩家重力下降
        if self.frame_time > FRAME_DURATION {
            self.frame_time = 0.0;
            self.player.gravity_and_move();
        }
        // 按空格键玩家跳跃
        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.player.flap();
        }
        self.player.render(ctx);
        thread::sleep(Duration::from_secs(1));
        ctx.print(0, 0, "Press Space to Flap");
        ctx.print(0, 5, &format!("Score: {}", self.score));
        self.obstacle.render(ctx, self.player.x);
        // 玩家通过障碍物则计分
        if self.player.x > self.obstacle.x {
            self.score += 1;
            self.obstacle = Obstacle::new(self.player.x + SCREEN_WDITH, self.score);
        }
        // 玩家撞到障碍物则游戏结束
        if self.player.y > SCREEN_HEIGHT || self.obstacle.hit_obstacle(&self.player) {
            self.mode = GameMode::End;
        }
    }

    // 游戏重新开始
    fn restart(&mut self) {
        self.player = Player::new(5, 25);
        self.frame_time = 0.0;
        self.mode = GameMode::Playing;
        self.obstacle = Obstacle::new(SCREEN_WDITH, 0);
        self.score = 0;
        println!("restart");
    }

    // 渲染主菜单
    fn main_menu(&mut self, ctx: &mut BTerm){
        ctx.cls();
        ctx.print_centered(5, "welcome to play rust game!");
        ctx.print_centered(10, "(P) Play again!");
        ctx.print_centered(15,  "(Q) Quit game!");

        // 监听键盘按键并执行游戏对应菜单
        if let Some(key) = ctx.key{
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    // 玩家死掉后执行的菜单
    fn dead(&mut self, ctx: &mut BTerm){
        ctx.cls();
        ctx.print_centered(5, "^*<>*^ You're died!");
        ctx.print_centered(10, &format!("Your Score:{}", self.score));
        ctx.print_centered(15, "(P) Play again!");
        ctx.print_centered(20,  "(Q) Quit game!");

        if let Some(key) = ctx.key{
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
}

// 实现游戏状态中tick的trait
impl GameState for State{
    // 游戏主循环
    fn tick(&mut self, ctx: &mut BTerm){
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::Playing => self.play(ctx),
            GameMode::End => self.dead(ctx),
        }
    }
}

// 开始游戏
pub fn run() -> BError{
    // 初始化界面、标题，并构建窗体
    let context: BTerm = BTermBuilder::simple80x50()
    .with_title("RUST游戏")
    .build()?;
    // 主循环
    main_loop(context, State::new())
}